let pi = acos (-1.)

class cam ?(maxX=1.) ?(maxY=1.)  ?(maxZ=1.)
  ?(minX=0.) ?(minY=0.) ?(minZ=0.) ()
  = object (self)
    val mutable teta = 0.
    val mutable phi = 0.
    val mutable gotoX = (maxX +. minX) /. 2.
    val mutable gotoY = (maxY +. minY) /. 2.
    val mutable gotoZ = maxZ *. 2.
    val mutable posX = (maxX +. minX) /. 2.
    val mutable posY = (maxY +. minY) /. 2.
    val mutable posZ = maxZ *. 2.
    val mutable gotoTeta = 0.
    val mutable gotoPhi = -90.
    val mutable freeFly = true
    val mutable coefVitesse = 4.5
    val mutable coefSlow = 20.
    val mutable coefSlowAngle = 20.
    val mutable maxX = maxX
    val mutable maxY = maxY
    val mutable maxZ = maxZ
    val mutable minX = minX
    val mutable minY = minY
    val mutable minZ = minZ


    method getPos () =
      ( posX, posY, posZ )
    method getFreeFly () = 
      freeFly

    method setFreeFly b = 
      freeFly <- b

    method reshape ~width:w ~height:h =
      GlDraw.viewport ~x:0 ~y:0 ~w ~h;
      GlMat.mode `projection;
      GlMat.load_identity ();
      GluMat.perspective ~fovy:60.0
        ~aspect:(float w /. float h)
        ~z:(1.,1000. );
      GlMat.mode `modelview

    method loop () =
      GlMat.mode `projection;
      posX <- posX +. ( gotoX -. posX) /. coefSlow ;
      posY <- posY +. ( gotoY -. posY ) /. coefSlow ;
      posZ <- posZ +. ( gotoZ -. posZ ) /. coefSlow ;
      teta <- teta +. ( gotoTeta -. teta  ) /. coefSlowAngle ;
      phi <- phi +. ( gotoPhi -. phi  ) /. coefSlowAngle ;
      GluMat.look_at
        ~eye:(posX,posY,posZ)
        ~center:(posX -. sin(teta*. pi /. 180.) *. cos(phi *. pi /. 180.),
                 posY +. cos(teta *. pi /. 180.) *. cos(phi *. pi /. 180.),
                 posZ +. sin(phi *. pi /. 180.))
        ~up:(0.,0.,1.);
      GlMat.mode `modelview

    method keyboardEvent k =
      if freeFly then
        begin
          if k =  GdkKeysyms._Up then self#forwardFreeFly ()
          else if k = GdkKeysyms._Down then self#backwardFreeFly ()
          else if k = GdkKeysyms._Left then self#leftFreeFly ()
          else if k = GdkKeysyms._Right then self#rightFreeFly ()
          else ()
        end
      else
        begin
          if k =  GdkKeysyms._Up then self#forward ()
          else if k = GdkKeysyms._Down then self#backward ()
          else if k = GdkKeysyms._Left then self#left ()
          else if k = GdkKeysyms._Right then self#right ()
          else ()
        end;
      if k =  GdkKeysyms._F1 then self#lookFromTop ()
      else if k =  GdkKeysyms._F2 then self#lookNearly ()
      else if k =  GdkKeysyms._F3 then self#lookFromTheRight ()
      else if k =  GdkKeysyms._F4 then self#lookFromTheLeft ()



    (* Method to move the camera in nomal mode *)

    method lookFromTop () =
      gotoY <- (maxY +. minY) /. 2.;
      gotoX <- (minX +. maxX) /. 2.;
      gotoZ <- (max maxX maxY) *. 2.  /. (2. *. (tan 0.52));
      gotoPhi <- -90.;


    method lookNearly () =
      gotoY <- (maxY +. minY) /. 2.;
      gotoX <- (minX +. maxX) /. 2.;
      gotoZ <- (max maxX maxY) /. (2. *. (tan 0.52));
      gotoPhi <- -90.;

    method lookFromTheRight () =
      gotoX <- maxX +. (maxY *. ( tan 0.52));
      gotoZ <- maxZ +. 10.;
      gotoY <- (maxY +. minY) /. 2.;
      gotoTeta <- 90.;
      gotoPhi <- -20.

    method lookFromTheLeft () =
      gotoX <- minX -. (maxY *. (tan 0.52));
      gotoZ <- maxZ +. 10.;
      gotoY <- (maxY +. minY) /. 2.;
      gotoTeta <- -90.;
      gotoPhi <- -20.

    (* Methods to call for the free fly camera; x and y represent the diference
       val mutable angleY = 0.
       * betwen the last and the current mouse position *)
    method freeFlyCameraAngle x y  =
      gotoPhi <- gotoPhi +. y/.2.;
      gotoTeta <- (gotoTeta +. x/.2.);

      if gotoTeta > 360. then
        begin
          gotoTeta <- 0.;
          teta <- teta -. 360.;
        end;
      if gotoTeta < -360. then
        begin
          gotoTeta <- 0.;
          teta <- teta +. 360.;
        end ;
      if gotoPhi < -90. then
        gotoPhi <- -90.;
      if gotoPhi > 90. then
        gotoPhi <- 90. 
    method forwardFreeFly () =
      gotoZ <- gotoZ +. coefVitesse *. sin(phi*. pi /. 180.);
      gotoX <- gotoX -. coefVitesse *. sin(teta*.pi/.180.);
      gotoY <- gotoY +. coefVitesse *. cos(teta*.pi/.180.)

    method backwardFreeFly () =
      gotoZ <- gotoZ -. coefVitesse *. sin(phi*. pi /. 180.);
      gotoX <- gotoX +. coefVitesse *. sin(teta*.pi/.180.);
      gotoY <- gotoY -. coefVitesse *. cos(teta*.pi/.180.)

    method rightFreeFly () =
      gotoY <- gotoY +. coefVitesse *. sin(teta *. pi /. 180.);
      gotoX <- gotoX +. coefVitesse *. cos(teta *. pi /. 180.)
    method leftFreeFly () =
      gotoY <- gotoY -. coefVitesse *. sin(teta *. pi /. 180.);
      gotoX <- gotoX -. coefVitesse *. cos(teta *. pi /. 180.)

    method forward () =
      gotoX <- gotoX +. 1.
    method backward () =
      gotoX <- gotoX -. 1.
    method left () =
      gotoY <- gotoY -. 1.
    method right () =
      gotoY <- gotoY +. 1.
  end
